home *** CD-ROM | disk | FTP | other *** search
/ Aminet 34 / Aminet 34 (2000)(Schatztruhe)[!][Dec 1999].iso / Aminet / util / gnu / unixcmds.lha / unixcmds / src / sort.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-06  |  40.5 KB  |  1,306 lines

  1. /*
  2. FUNCTIONS
  3. This source file includes following functions.
  4. get_opts
  5. new_field
  6. main
  7. adjust_options
  8. error
  9. open_outfile
  10. get_file
  11. last_line
  12. print_table
  13. file_name
  14. mread
  15. mwrite
  16. sort
  17. sort_table
  18. incr
  19. cmp_fields
  20. build_field
  21. skip_fields
  22. compare
  23. cmp
  24. digits
  25. files_merge
  26. merge
  27. put_line
  28. print
  29. read_line
  30. skip_lines
  31. uniq_lines
  32. check_file
  33. length
  34. copy
  35. msbrk
  36. mbrk
  37. catch
  38. */
  39.  
  40. /* sort - sort a file of lines          Author: Michiel Huisjes */
  41.  
  42. /* SYNOPSIS:
  43.  *      sort [-funbirdcmt'x'] [+beg_pos[opts] [-end_pos]] [-o outfile] [file]..
  44.  *
  45.  *      [opts] can be any of
  46.  *      -f : Fold upper case to lower.
  47.  *      -n : Sort to numeric value (optional decimal point) implies -b
  48.  *      -b : Skip leading blanks
  49.  *      -i : Ignore chars outside ASCII range (040 - 0176)
  50.  *      -r : Reverse the sense of comparisons.
  51.  *      -d : Sort to dictionary order. Only letters, digits, comma's and points
  52.  *           are compared.
  53.  *      If any of these flags are used in [opts], then they override all global
  54.  *      ordering for this field.
  55.  *
  56.  *      I/O control flags are:
  57.  *      -u : Print uniq lines only once.
  58.  *      -c : Check if files are sorted in order.
  59.  *      -m : Merge already sorted files.
  60.  *      -o outfile : Name of output file. (Can be one of the input files).
  61.  *                   Default is stdout.
  62.  *      - : Take stdin as input.
  63.  *
  64.  *      Fields:
  65.  *      -t'x' : Field separating character is 'x'
  66.  *      +a.b : Start comparing at field 'a' with offset 'b'. A missing 'b' is
  67.  *             taken to be 0.
  68.  *      -a.b : Stop comparing at field 'a' with offset 'b'. A missing 'b' is
  69.  *             taken to be 0.
  70.  *      A missing -a.b means the rest of the line.
  71.  */
  72.  
  73. #include <sys/types.h>
  74. #include <sys/stat.h>
  75. #include <fcntl.h>
  76. #include <signal.h>
  77. #include <unistd.h>
  78. #include <stdlib.h>
  79. #include <string.h>
  80. #include <stdio.h>
  81. #include <limits.h>
  82.  
  83. #include "amigawildcard.h"
  84.  
  85. #define OPEN_MAX        50
  86.  
  87. #define OPEN_FILES      (OPEN_MAX-4)    /* Nr of open files per process */
  88. #define MEMORY_SIZE     (20 * 1024)     /* Total mem_size */
  89. #define LINE_SIZE       (1024 >> 1)     /* Max length of a line */
  90. #define IO_SIZE         (2 * 1024)      /* Size of buffered output */
  91. #define STD_OUT          1      /* Fd of terminal */
  92.  
  93. /* Return status of functions */
  94. #define OK               0
  95. #define ERROR           -1
  96. #define NIL_PTR         ((char *) 0)
  97.  
  98. /* Compare return values */
  99. #define LOWER           -1
  100. #define SAME             0
  101. #define HIGHER           1
  102.  
  103. /* Table definitions. */
  104. #define DICT            0x001   /* Alpha, numeric, letters and . */
  105. #define ASCII           0x002   /* All between ' ' and '~' */
  106. #define BLANK           0x004   /* ' ' and '\t' */
  107. #define DIGIT           0x008   /* 0-9 */
  108. #define UPPER           0x010   /* A-Z */
  109.  
  110. typedef int Bool;
  111.  
  112. #define FALSE   0
  113. #define TRUE    1
  114.  
  115. typedef struct {
  116.   int fd;                       /* Fd of file */
  117.   char *buffer;                 /* Buffer for reads */
  118.   int read_chars;               /* Nr of chars actually read in buffer */
  119.   int cnt;                      /* Nr of chars taken out of buffer */
  120.   char *line;                   /* Contains line currently used */
  121. } MERGE;
  122.  
  123. #define NIL_MERGE       ((MERGE *) 0)
  124. MERGE merge_f[OPEN_FILES];      /* Merge structs */
  125. int buf_size;                   /* Size of core available for each struct */
  126.  
  127. #define FIELDS_LIMIT    10      /* 1 global + 9 user */
  128. #define sort_GLOBAL           0
  129.  
  130. typedef struct {
  131.   int beg_field, beg_pos;       /* Begin field + offset */
  132.   int end_field, end_pos;       /* End field + offset. ERROR == EOLN */
  133.   Bool reverse;                 /* TRUE if rev. flag set on this field */
  134.   Bool blanks;
  135.   Bool dictionary;
  136.   Bool fold_case;
  137.   Bool ascii;
  138.   Bool numeric;
  139. } FIELD;
  140.  
  141. /* Field declarations. A total of FILEDS_LIMIT is allowed */
  142. FIELD fields[FIELDS_LIMIT];
  143. int field_cnt;                  /* Nr of field actually assigned */
  144.  
  145. /* Various output control flags */
  146. Bool check = FALSE;
  147. Bool only_merge = FALSE;
  148. Bool uniq = FALSE;
  149.  
  150. char *mem_top;                  /* Mem_top points to lowest pos of memory. */
  151. char *cur_pos;                  /* First free position in mem */
  152. char **line_table;              /* Pointer to the internal line table */
  153. Bool in_core = TRUE;            /* Set if input cannot all be sorted in core */
  154.  
  155.  /* Place where temp_files should be made */
  156. char temp_files[] = "/tmp/sort.XXXXX.XX";
  157. char *output_file;              /* Name of output file */
  158. int out_fd;                     /* Fd to output file (could be STD_OUT) */
  159. char out_buffer[IO_SIZE];       /* For buffered output */
  160.  
  161. int args_offset;                /* Nr of args spilled on options */
  162. int args_limit;                 /* Nr of args given */
  163.  
  164. char separator;                 /* Char that separates fields */
  165. int nr_of_files = 0;            /* Nr_of_files to be merged */
  166. int disabled;                   /* Nr of files done */
  167.  
  168. t_strlist *strl;
  169.  
  170. char USAGE[] = "Usage: sort [-funbirdcmt'x'] [+beg_pos [-end_pos]] [-o outfile] [file] ..\n";
  171.  
  172. /* Forward declarations */
  173. int main  (int argc, char **argv);
  174. void get_opts  (char *ptr, FIELD * field);
  175. void new_field  (FIELD * field, int *offset, Bool beg_fl);
  176. void adjust_options  (FIELD * field);
  177. void error  (Bool quit, char *message, char *arg);
  178. void open_outfile  (void);
  179. void get_file  (int fd, off_t size);
  180. int last_line  (void);
  181. void print_table  (int fd);
  182. char *file_name  (int nr);
  183. void mread  (int fd, char *address, int bytes);
  184. void mwrite  (int fd, char *address, int bytes);
  185. void sort  (void);
  186. void sort_table  (int nel);
  187. void incr  (int si, int ei);
  188. int cmp_fields  (char *el1, char *el2);
  189. void build_field  (char *dest, FIELD * field, char *src);
  190. char *skip_fields  (char *str, int nf);
  191. int compare  (char *el1, char *el2);
  192. int cmp  (unsigned char *el1, unsigned char *el2, FIELD * field);
  193. int digits  (char *str1, char *str2, Bool check_sign);
  194. void files_merge  (int file_cnt);
  195. void merge  (int start_file, int limit_file);
  196. void put_line  (char *line);
  197. MERGE * print  (MERGE * merg, int file_cnt);
  198. int read_line  (MERGE * merg);
  199. MERGE * skip_lines  (MERGE * smallest, int file_cnt);
  200. void uniq_lines  (MERGE * merg);
  201. void check_file  (int fd, char *file);
  202. int length  (char *line);
  203. void copy  (char *dest, char *src);
  204. char *msbrk  (int size);
  205. void mbrk  (char *address);
  206. void catch  (int dummy);
  207. int oldmain(int argc, t_strlist *strl);
  208. void do_exit(int retcode);
  209.  
  210. /* Table of all chars. 0 means no special meaning. */
  211. char table[256] = {
  212. /* '^@' to space */
  213.            0, 0, 0, 0, 0, 0, 0, 0,
  214.            0, BLANK | DICT, 0, 0, 0, 0, 0, 0,
  215.            0, 0, 0, 0, 0, 0, 0, 0,
  216.            0, 0, 0, 0, 0, 0, 0, 0,
  217.  
  218. /* Space to '0' */
  219.        BLANK | DICT | ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
  220.            ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
  221.            ASCII, ASCII,
  222.  
  223. /* '0' until '9' */
  224.      DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII,
  225.      DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII,
  226.      DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII,
  227.            DIGIT | DICT | ASCII,
  228.  
  229. /* ASCII from ':' to '@' */
  230.            ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
  231.  
  232. /* Upper case letters 'A' to 'Z' */
  233.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  234.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  235.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  236.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  237.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  238.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  239.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  240.      UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  241.            UPPER | DICT | ASCII, UPPER | DICT | ASCII,
  242.  
  243. /* ASCII from '[' to '`' */
  244.            ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
  245.  
  246. /* Lower case letters from 'a' to 'z' */
  247.            DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
  248.            DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
  249.            DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
  250.            DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
  251.            DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
  252.            DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
  253.            DICT | ASCII, DICT | ASCII,
  254.  
  255. /* ASCII from '{' to '~' */
  256.            ASCII, ASCII, ASCII, ASCII,
  257.  
  258. /* Stuff from -1 to -177 */
  259.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  260.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  261.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  262.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  263.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  264.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  265.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  266.            0, 0, 0, 0, 0, 0, 0, 0, 0,
  267.            0, 0, 0, 0, 0, 0, 0
  268. };
  269.  
  270.  
  271. /*
  272.  * Get_opts () assigns the options into the field structure as described in ptr.
  273.  * This field structure could be the sort_GLOBAL one.
  274.  */
  275. void get_opts(ptr, field)
  276. /* [<][>][^][v][top][bottom][index][help] */
  277. register char *ptr;
  278. register FIELD *field;
  279. {
  280.   switch (*ptr) {
  281.       case 'b':                 /* Skip leading blanks */
  282.         field->blanks = TRUE;
  283.         break;
  284.       case 'd':                 /* Dictionary order */
  285.         field->dictionary = TRUE;
  286.         break;
  287.       case 'f':                 /* Fold upper case to lower */
  288.         field->fold_case = TRUE;
  289.         break;
  290.       case 'i':                 /* Skip chars outside ' ' '~' */
  291.         field->ascii = TRUE;
  292.         break;
  293.       case 'n':                 /* Sort on numeric */
  294.         field->numeric = TRUE;
  295.         field->blanks = TRUE;
  296.         break;
  297.       case 'r':                 /* Reverse comparisons */
  298.         field->reverse = TRUE;
  299.         break;
  300.       default:                  /* Illegal options */
  301.         error(TRUE, USAGE, NIL_PTR);
  302.   }
  303. }
  304.  
  305. /* New_field () assigns a new field as described by the arguments.
  306.  * A field description is of the form: +a.b[opts] -c.d, where b and d, as well
  307.  * as -c.d and [opts] are optional. Nr before digit is field nr. Nr after digit
  308.  * is offset from field.
  309.  */
  310. void new_field(field, offset, beg_fl)
  311. /* [<][>][^][v][top][bottom][index][help] */
  312. register FIELD *field;          /* Field to assign */
  313. int *offset;                    /* Offset in argv structure */
  314. Bool beg_fl;                    /* Assign beg or end of field */
  315. {
  316.   register char *ptr;
  317.  
  318.   ptr = pop_elt(*offset,strl);
  319.   *offset += 1;                 /* Incr offset to next arg */
  320.   ptr++;
  321.  
  322.   if (beg_fl)
  323.         field->beg_field = atoi(ptr);   /* Assign int of first field */
  324.   else
  325.         field->end_field = atoi(ptr);
  326.  
  327.   while (table[*ptr] & DIGIT)   /* Skip all digits */
  328.         ptr++;
  329.  
  330.   if (*ptr == '.') {            /* Check for offset */
  331.         ptr++;
  332.         if (beg_fl)
  333.                 field->beg_pos = atoi(ptr);
  334.         else
  335.                 field->end_pos = atoi(ptr);
  336.         while (table[*ptr] & DIGIT)     /* Skip digits */
  337.                 ptr++;
  338.   }
  339.   if (beg_fl) {
  340.         while (*ptr != '\0')    /* Check options after field */
  341.                 get_opts(ptr++, field);
  342.   }
  343.   if (beg_fl) {                 /* Check for end pos */
  344.         ptr = pop_elt(*offset,strl);
  345.         if (ptr && *ptr == '-' && table[*(ptr + 1)] & DIGIT) {
  346.                 new_field(field, offset, FALSE);
  347.                 if (field->beg_field > field->end_field)
  348.                         error(TRUE, "End field is before start field!", NIL_PTR);
  349.         } else                  /* No end pos. */
  350.                 field->end_field = ERROR;
  351.   }
  352. }
  353.  
  354. void do_exit(int retcode)
  355. {
  356.     /* frees the argument memory if any */
  357.  
  358.     clear_list(strl);
  359.  
  360.     /* and exits */
  361.  
  362.     exit (retcode);
  363. }
  364.  
  365. /* JF Fabre: moved the main so it can preprocess
  366.    amiga wildcards */
  367.  
  368. int main(int argc,char *argv[])
  369. {
  370.     t_strlist real_strl;
  371.     int ret;
  372.  
  373.     strl=&real_strl;
  374.  
  375.     init_list(strl);
  376.  
  377.     fill_list(argv,0,argc,strl);
  378.  
  379.     ret=oldmain(strl->len,strl);
  380.  
  381.     do_exit(ret);
  382.  
  383. }
  384.  
  385.  
  386. int oldmain(int argc, t_strlist *strl)
  387. /* [<][>][^][v][top][bottom][index][help] */
  388. {
  389.   int arg_count = 1;            /* Offset in argv */
  390.   struct stat st;
  391.   register char *ptr;           /* Ptr to *argv in use */
  392.   register int fd;
  393.   int pid, pow;
  394.  
  395.   cur_pos = mem_top = msbrk(MEMORY_SIZE);       /* Find lowest mem. location */
  396.  
  397.   while (arg_count < argc && ((ptr = pop_elt(arg_count,strl))[0] == '-' || *ptr == '+')) {
  398.         if (*ptr == '-' && *(ptr + 1) == '\0')  /* "-" means stdin */
  399.                 break;
  400.         if (*ptr == '+') {      /* Assign field. */
  401.                 if (++field_cnt == FIELDS_LIMIT)
  402.                         error(TRUE, "Too many fields", NIL_PTR);
  403.                 new_field(&fields[field_cnt], &arg_count, TRUE);
  404.         } else {                /* Get output options */
  405.                 while (*++ptr) {
  406.                         switch (*ptr) {
  407.                             case 'c':   /* Only check file */
  408.                                 check = TRUE;
  409.                                 break;
  410.                             case 'm':   /* Merge (sorted) files */
  411.                                 only_merge = TRUE;
  412.                                 break;
  413.                             case 'u':   /* Only give uniq lines */
  414.                                 uniq = TRUE;
  415.                                 break;
  416.                             case 'o':   /* Name of output file */
  417.                                 output_file = pop_elt(++arg_count,strl);
  418.                                 break;
  419.                             case 't':   /* Field separator */
  420.                                 ptr++;
  421.                                 separator = *ptr;
  422.                                 break;
  423.                             default:    /* Sort options */
  424.                                 get_opts(ptr, &fields[sort_GLOBAL]);
  425.                         }
  426.                 }
  427.                 arg_count++;
  428.         }
  429.   }
  430.  
  431.   for (fd = 1; fd <= field_cnt; fd++) adjust_options(&fields[fd]);
  432.  
  433. /* Create name of tem_files 'sort.pid.aa' */
  434.   ptr = &temp_files[10];
  435.   /*pid = getpid();*/
  436.   pid = 1000;
  437.  
  438.   pow = 10000;
  439.   while (pow != 0) {
  440.         *ptr++ = pid / pow + '0';
  441.         pid %= pow;
  442.         pow /= 10;
  443.   }
  444.  
  445.   signal(SIGINT, catch);
  446.  
  447. /* Only merge files. Set up */
  448.   if (only_merge) {
  449.         args_limit = args_offset = arg_count;
  450.         while (pop_elt(args_limit,strl) != NIL_PTR)
  451.                 args_limit++;   /* Find nr of args */
  452.         files_merge(args_limit - arg_count);
  453.         return(0);
  454.   }
  455.   if (arg_count == argc) {      /* No args left. Use stdin */
  456.         if (check)
  457.                 check_file(0, NIL_PTR);
  458.         else
  459.                 get_file(0, (off_t) 0);
  460.   } else
  461.         while (arg_count < argc) {      /* Sort or check args */
  462.                 if (strcmp(pop_elt(arg_count,strl), "-") == 0)
  463.                         fd = 0;
  464.                 else if (stat(pop_elt(arg_count,strl), &st) < 0) {
  465.                         error(FALSE, "Cannot find ", pop_elt(arg_count++,strl));
  466.                         continue;
  467.                 }
  468.  
  469.                 /* Open files */
  470.                 else if ((fd = open(pop_elt(arg_count,strl), O_RDONLY)) < 0) {
  471.                         error(FALSE, "Cannot open ", pop_elt(arg_count++,strl));
  472.                         continue;
  473.                 }
  474.                 if (check)
  475.                         check_file(fd, pop_elt(arg_count,strl));
  476.                 else            /* Get_file reads whole file */
  477.                         get_file(fd, st.st_size);
  478.                 arg_count++;
  479.         }
  480.  
  481.   if (check) return(0);
  482.  
  483.   sort();                       /* Sort whatever is left */
  484.  
  485.   if (nr_of_files == 1)         /* Only one file sorted -> don't merge */
  486.         return(0);
  487.  
  488.   files_merge(nr_of_files);
  489.   return(0);
  490. }
  491.  
  492. /* Adjust_options() assigns all global variables set also in the fields
  493.  * assigned.
  494.  */
  495. void adjust_options(field)
  496. /* [<][>][^][v][top][bottom][index][help] */
  497. register FIELD *field;
  498. {
  499.   register FIELD *gfield = &fields[sort_GLOBAL];
  500.  
  501.   if (gfield->reverse) field->reverse = TRUE;
  502.   if (gfield->blanks) field->blanks = TRUE;
  503.   if (gfield->dictionary) field->dictionary = TRUE;
  504.   if (gfield->fold_case) field->fold_case = TRUE;
  505.   if (gfield->ascii) field->ascii = TRUE;
  506.   if (gfield->numeric) field->numeric = TRUE;
  507. }
  508.  
  509. /* Error () prints the error message on stderr and exits if quit == TRUE. */
  510. void error(quit, message, arg)
  511. /* [<][>][^][v][top][bottom][index][help] */
  512. register Bool quit;
  513. register char *message, *arg;
  514. {
  515.   write(2, message, strlen(message));
  516.   if (arg != NIL_PTR) write(2, arg, strlen(arg));
  517.   perror(" ");
  518.   if (quit) do_exit(1);
  519. }
  520.  
  521. /* Open_outfile () assigns to out_fd the fd where the output must go when all
  522.  * the sorting is done.
  523.  */
  524. void open_outfile()
  525. /* [<][>][^][v][top][bottom][index][help] */
  526. {
  527.   if (output_file == NIL_PTR)
  528.         out_fd = STD_OUT;
  529.   else if ((out_fd = creat(output_file, 0644)) < 0)
  530.         error(TRUE, "Cannot creat ", output_file);
  531. }
  532.  
  533. /* Get_file reads the whole file of filedescriptor fd. If the file is too big
  534.  * to keep in core, a partial sort is done, and the output is stashed somewhere.
  535.  */
  536. void get_file(fd, size)
  537. /* [<][>][^][v][top][bottom][index][help] */
  538. int fd;                         /* Fd of file to read */
  539. register off_t size;            /* Size of file */
  540. {
  541.   register int i;
  542.   int rest;                     /* Rest in memory */
  543.   char save_ch;                 /* Used in stdin readings */
  544.  
  545.   rest = MEMORY_SIZE - (cur_pos - mem_top);
  546.   if (fd == 0) {                /* We're reding stdin */
  547.         while ((i = read(0, cur_pos, rest)) > 0) {
  548.                 if ((cur_pos - mem_top) + i == MEMORY_SIZE) {
  549.                         in_core = FALSE;
  550.                         i = last_line();        /* End of last line */
  551.                         save_ch = mem_top[i];
  552.                         mem_top[i] = '\0';
  553.                         sort(); /* Sort core */
  554.                         mem_top[i] = save_ch;   /* Restore erased char */
  555.                         /* Restore last (half read) line */
  556.                         for (rest = 0; i + rest != MEMORY_SIZE; rest++)
  557.                                 mem_top[rest] = mem_top[i + rest];
  558.                         /* Assign current pos. in memory */
  559.                         cur_pos = &mem_top[rest];
  560.                 } else {        /* Fits, just assign position in mem. */
  561.                         cur_pos = cur_pos + i;
  562.                         *cur_pos = '\0';
  563.                 }
  564.  
  565.                 /* Calculate rest of mem */
  566.                 rest = MEMORY_SIZE - (cur_pos - mem_top);
  567.         }
  568.   }
  569.  
  570.   /* Reading file. Check size */
  571.   else if (size > rest) {       /* Won't fit */
  572.         mread(fd, cur_pos, rest);
  573.         in_core = FALSE;
  574.         i = last_line();        /* Get pos. of last line */
  575.         mem_top[i] = '\0';      /* Truncate */
  576.         (void) lseek(fd, (off_t) (i - MEMORY_SIZE), SEEK_CUR);  /* Do this next time */
  577.         size = size - rest - i + MEMORY_SIZE;   /* Calculate rest */
  578.         cur_pos = mem_top;      /* Reset mem */
  579.         sort();                 /* Sort core */
  580.         get_file(fd, size);     /* Get rest of file */
  581.   } else {                      /* Fits. Just read in */
  582.         rest = size;
  583.         mread(fd, cur_pos, rest);
  584.         cur_pos = cur_pos + rest;       /* Reassign cur_pos */
  585.         *cur_pos = '\0';
  586.         (void) close(fd);       /* File completed */
  587.   }
  588. }
  589.  
  590. /* Last_line () find the last line in core and retuns the offset from the top
  591.  * of the memory.
  592.  */
  593. int last_line()
  594. /* [<][>][^][v][top][bottom][index][help] */
  595. {
  596.   register int i;
  597.  
  598.   for (i = MEMORY_SIZE - 2; i > 0; i--)
  599.         if (mem_top[i] == '\n') break;
  600.   return i + 1;
  601. }
  602.  
  603. /* Print_table prints the line table in the given file_descriptor. If the fd
  604.  * equals ERROR, it opens a temp_file itself.
  605.  */
  606. void print_table(fd)
  607. /* [<][>][^][v][top][bottom][index][help] */
  608. int fd;
  609. {
  610.   register char **line_ptr;     /* Ptr in line_table */
  611.   register char *ptr;           /* Ptr to line */
  612.   int index = 0;                /* Index in output buffer */
  613.  
  614.   if (fd == ERROR) {
  615.         if ((fd = creat(file_name(nr_of_files), 0644)) < 0)
  616.                 error(TRUE, "Cannot creat ", file_name(nr_of_files));
  617.   }
  618.   for (line_ptr = line_table; *line_ptr != NIL_PTR; line_ptr++) {
  619.         ptr = *line_ptr;
  620.         /* Skip all same lines if uniq is set */
  621.         if (uniq && *(line_ptr + 1) != NIL_PTR) {
  622.                 if (compare(ptr, *(line_ptr + 1)) == SAME) continue;
  623.         }
  624.         do {                    /* Print line in a buffered way */
  625.                 out_buffer[index++] = *ptr;
  626.                 if (index == IO_SIZE) {
  627.                         mwrite(fd, out_buffer, IO_SIZE);
  628.                         index = 0;
  629.                 }
  630.         } while (*ptr++ != '\n');
  631.   }
  632.   mwrite(fd, out_buffer, index);/* Flush buffer */
  633.   (void) close(fd);             /* Close file */
  634.   nr_of_files++;                /* Increment nr_of_files to merge */
  635. }
  636.  
  637. /* File_name () returns the nr argument from the argument list, or a uniq
  638.  * filename if the nr is too high, or the arguments were not merge files.
  639.  */
  640. char *file_name(nr)
  641. /* [<][>][^][v][top][bottom][index][help] */
  642. register int nr;
  643. {
  644.   if (only_merge) {
  645.         if (args_offset + nr < args_limit) return pop_elt(args_offset + nr,strl);
  646.   }
  647.   temp_files[16] = nr / 26 + 'a';
  648.   temp_files[17] = nr % 26 + 'a';
  649.  
  650.   return temp_files;
  651. }
  652.  
  653. /* Mread () performs a normal read (), but checks the return value. */
  654. void mread(fd, address, bytes)
  655. /* [<][>][^][v][top][bottom][index][help] */
  656. int fd;
  657. char *address;
  658. register int bytes;
  659. {
  660.   if (read(fd, address, bytes) < 0 && bytes != 0)
  661.         error(TRUE, "Read error", NIL_PTR);
  662. }
  663.  
  664. /* Mwrite () performs a normal write (), but checks the return value. */
  665. void mwrite(fd, address, bytes)
  666. /* [<][>][^][v][top][bottom][index][help] */
  667. int fd;
  668. char *address;
  669. register int bytes;
  670. {
  671.   if (write(fd, address, bytes) != bytes && bytes != 0)
  672.         error(TRUE, "Write error", NIL_PTR);
  673. }
  674.  
  675. /* Sort () sorts the input in memory starting at mem_top. */
  676. void sort()
  677. /* [<][>][^][v][top][bottom][index][help] */
  678. {
  679.   register char *ptr = mem_top;
  680.   register int count = 0;
  681.  
  682. /* Count number of lines in memory */
  683.   while (*ptr) {
  684.         if (*ptr++ == '\n') count++;
  685.   }
  686.  
  687. /* Set up the line table */
  688.   line_table = (char **) msbrk(count * sizeof(char *) + sizeof(char *));
  689.  
  690.   count = 1;
  691.   ptr = line_table[0] = mem_top;
  692.   while (*ptr) {
  693.         if (*ptr++ == '\n') line_table[count++] = ptr;
  694.   }
  695.  
  696.   line_table[count - 1] = NIL_PTR;
  697.  
  698. /* Sort the line table */
  699.   sort_table(count - 1);
  700.  
  701. /* Stash output somewhere */
  702.   if (in_core) {
  703.         open_outfile();
  704.         print_table(out_fd);
  705.   } else
  706.         print_table(ERROR);
  707.  
  708. /* Free line table */
  709.   mbrk((char *) line_table);
  710. }
  711.  
  712. /* Sort_table () sorts the line table consisting of nel elements. */
  713. void sort_table(nel)
  714. /* [<][>][^][v][top][bottom][index][help] */
  715. register int nel;
  716. {
  717.   char *tmp;
  718.   register int i;
  719.  
  720.   /* Make heap */
  721.   for (i = (nel >> 1); i >= 1; i--) incr(i, nel);
  722.  
  723.   /* Sort from heap */
  724.   for (i = nel; i > 1; i--) {
  725.         tmp = line_table[0];
  726.         line_table[0] = line_table[i - 1];
  727.         line_table[i - 1] = tmp;
  728.         incr(1, i - 1);
  729.   }
  730. }
  731.  
  732. /* Incr () increments the heap. */
  733. void incr(si, ei)
  734. /* [<][>][^][v][top][bottom][index][help] */
  735. register int si, ei;
  736. {
  737.   char *tmp;
  738.  
  739.   while (si <= (ei >> 1)) {
  740.         si <<= 1;
  741.         if (si + 1 <= ei && compare(line_table[si - 1], line_table[si]) <= 0)
  742.                 si++;
  743.         if (compare(line_table[(si >> 1) - 1], line_table[si - 1]) >= 0)
  744.                 return;
  745.         tmp = line_table[(si >> 1) - 1];
  746.         line_table[(si >> 1) - 1] = line_table[si - 1];
  747.         line_table[si - 1] = tmp;
  748.   }
  749. }
  750.  
  751. /* Cmp_fields builds new lines out of the lines pointed to by el1 and el2 and
  752.  * puts it into the line1 and line2 arrays. It then calls the cmp () routine
  753.  * with the field describing the arguments.
  754.  */
  755. int cmp_fields(el1, el2)
  756. /* [<][>][^][v][top][bottom][index][help] */
  757. register char *el1, *el2;
  758. {
  759.   int i, ret;
  760.   char line1[LINE_SIZE], line2[LINE_SIZE];
  761.  
  762.   for (i = 0; i < field_cnt; i++) {     /* Setup line parts */
  763.         build_field(line1, &fields[i + 1], el1);
  764.         build_field(line2, &fields[i + 1], el2);
  765.         if ((ret = cmp((unsigned char *) line1, (unsigned char *) line2,
  766.                        &fields[i + 1])) != SAME)
  767.                 break;          /* If equal, try next field */
  768.   }
  769.  
  770. /* Check for reverse flag */
  771.   if (i != field_cnt && fields[i + 1].reverse) return -ret;
  772.  
  773. /* Else return the last return value of cmp () */
  774.   return ret;
  775. }
  776.  
  777. /* Build_field builds a new line from the src as described by the field.
  778.  * The result is put in dest.
  779.  */
  780. void build_field(dest, field, src)
  781. /* [<][>][^][v][top][bottom][index][help] */
  782. char *dest;                     /* Holds result */
  783. register FIELD *field;          /* Field description */
  784. register char *src;             /* Source line */
  785. {
  786.   char *begin = src;            /* Remember start location */
  787.   char *last;                   /* Pointer to end location */
  788.   int i;
  789.  
  790. /* Skip begin fields */
  791.   src = skip_fields(src, field->beg_field);
  792.  
  793. /* Skip begin positions */
  794.   for (i = 0; i < field->beg_pos && *src != '\n'; i++) src++;
  795.  
  796. /* Copy whatever is left */
  797.   copy(dest, src);
  798.  
  799. /* If end field is assigned truncate (perhaps) the part copied */
  800.   if (field->end_field != ERROR) {      /* Find last field */
  801.         last = skip_fields(begin, field->end_field);
  802. /* Skip positions as given by end fields description */
  803.         for (i = 0; i < field->end_pos && *last != '\n'; i++) last++;
  804.         dest[last - src] = '\n';/* Truncate line */
  805.   }
  806. }
  807.  
  808. /* Skip_fields () skips nf fields of the line pointed to by str. */
  809. char *skip_fields(str, nf)
  810. /* [<][>][^][v][top][bottom][index][help] */
  811. register char *str;
  812. int nf;
  813. {
  814.   while (nf-- > 0) {
  815.         if (separator == '\0') {/* Means ' ' or '\t' */
  816.                 while (table[*str] & BLANK) str++;
  817.                 while (*str != ' ' && *str != '\t' && *str != '\n') str++;
  818.         } else {
  819.                 while (*str == separator) str++;
  820.                 while (*str != separator && *str != '\n') str++;
  821.         }
  822.   }
  823.   return str;                   /* Return pointer to indicated field */
  824. }
  825.  
  826. /* Compare is called by all sorting routines. It checks if fields assignments
  827.  * has been made. if so, it calls cmp_fields (). If not, it calls cmp () and
  828.  * reversed the return value if the (global) reverse flag is set.
  829.  */
  830. int compare(el1, el2)
  831. /* [<][>][^][v][top][bottom][index][help] */
  832. register char *el1, *el2;
  833. {
  834.   int ret;
  835.  
  836.   if (field_cnt > sort_GLOBAL) return cmp_fields(el1, el2);
  837.  
  838.   ret = cmp((unsigned char *) el1, (unsigned char *) el2, &fields[sort_GLOBAL]);
  839.   return(fields[sort_GLOBAL].reverse) ? -ret : ret;
  840. }
  841.  
  842. /* Cmp () is the actual compare routine. It compares according to the
  843.  * description given in the field pointer.
  844.  */
  845. int cmp(el1, el2, field)
  846. /* [<][>][^][v][top][bottom][index][help] */
  847. register unsigned char *el1, *el2;
  848. FIELD *field;
  849. {
  850.   int c1, c2;
  851.  
  852.   if (field->blanks) {          /* Skip leading blanks */
  853.         while (table[*el1] & BLANK) el1++;
  854.         while (table[*el2] & BLANK) el2++;
  855.   }
  856.   if (field->numeric)           /* Compare numeric */
  857.         return digits((char *) el1, (char *) el2, TRUE);
  858.  
  859.   for (;;) {
  860.         while (*el1 == *el2) {
  861.                 if (*el1++ == '\n')     /* EOLN on both strings */
  862.                         return SAME;
  863.                 el2++;
  864.         }
  865.         if (*el1 == '\n')       /* EOLN on string one */
  866.                 return LOWER;
  867.         if (*el2 == '\n') return HIGHER;
  868.         if (field->ascii) {     /* Skip chars outside 040 - 0177 */
  869.                 if ((table[*el1] & ASCII) == 0) {
  870.                         do {
  871.                                 el1++;
  872.                         } while ((table[*el1] & ASCII) == 0);
  873.                         continue;
  874.                 }
  875.                 if ((table[*el2] & ASCII) == 0) {
  876.                         do {
  877.                                 el2++;
  878.                         } while ((table[*el2] & ASCII) == 0);
  879.                         continue;
  880.                 }
  881.         }
  882.         if (field->dictionary) {/* Skip non-dict chars */
  883.                 if ((table[*el1] & DICT) == 0) {
  884.                         do {
  885.                                 el1++;
  886.                         } while ((table[*el1] & DICT) == 0);
  887.                         continue;
  888.                 }
  889.                 if ((table[*el2] & DICT) == 0) {
  890.                         do {
  891.                                 el2++;
  892.                         } while ((table[*el2] & DICT) == 0);
  893.                         continue;
  894.                 }
  895.         }
  896.         if (field->fold_case) { /* Fold upper case to lower */
  897.                 if (table[c1 = *el1++] & UPPER) c1 += 'a' - 'A';
  898.                 if (table[c2 = *el2++] & UPPER) c2 += 'a' - 'A';
  899.                 if (c1 == c2) continue;
  900.                 return c1 - c2;
  901.         }
  902.         return *el1 - *el2;
  903.   }
  904.  
  905.   /* NOTREACHED */
  906. }
  907.  
  908. /*
  909.  * Digits compares () the two strings that point to a number of digits followed
  910.  * by an optional decimal point.
  911.  */
  912. int digits(str1, str2, check_sign)
  913. /* [<][>][^][v][top][bottom][index][help] */
  914. register char *str1, *str2;
  915. Bool check_sign;                /* True if sign must be checked */
  916. {
  917.   Bool negative = FALSE;        /* True if negative numbers */
  918.   int diff, pow, ret;
  919.  
  920. /* Check for optional minus or plus sign */
  921.   if (check_sign) {
  922.         if (*str1 == '-') {
  923.                 negative = TRUE;
  924.                 str1++;
  925.         } else if (*str1 == '+')
  926.                 str1++;
  927.  
  928.         if (*str2 == '-') {
  929.                 if (negative == FALSE) return HIGHER;
  930.                 str2++;
  931.         } else if (negative)
  932.                 return LOWER;
  933.         else if (*str2 == '+')
  934.                 str2++;
  935.   }
  936.  
  937. /* Keep incrementing as long as digits are available and equal */
  938.   while ((table[*str1] & DIGIT) && table[*str2] & DIGIT) {
  939.         if (*str1 != *str2) break;
  940.         str1++;
  941.         str2++;
  942.   }
  943.  
  944. /* First check for the decimal point. */
  945.   if (*str1 == '.' || *str2 == '.') {
  946.         if (*str1 == '.') {
  947.                 if (*str2 == '.')       /* Both. Check decimal part */
  948.                         ret = digits(str1 + 1, str2 + 1, FALSE);
  949.                 else
  950.                         ret = (table[*str2] & DIGIT) ? LOWER : HIGHER;
  951.         } else
  952.                 ret = (table[*str1] & DIGIT) ? HIGHER : LOWER;
  953.   }
  954.  
  955. /* Now either two digits differ, or unknown char is seen (e.g. end of string) */
  956.   else if ((table[*str1] & DIGIT) && (table[*str2] & DIGIT)) {
  957.         diff = *str1 - *str2;   /* Basic difference */
  958.         pow = 0;                /* Check power of numbers */
  959.         while (table[*str1++] & DIGIT) pow++;
  960.         while (table[*str2++] & DIGIT) pow--;
  961.         ret = (pow == 0) ? diff : pow;
  962.   }
  963.  
  964. /* Unknown char. Check on which string it occurred */
  965.   else {
  966.         if ((table[*str1] & DIGIT) == 0)
  967.                 ret = (table[*str2] & DIGIT) ? LOWER : SAME;
  968.         else
  969.                 ret = HIGHER;
  970.   }
  971.  
  972. /* Reverse sense of comparisons if negative is true. (-1000 < -1) */
  973.   return(negative) ? -ret : ret;
  974. }
  975.  
  976. /* Files_merge () merges all files as indicated by nr_of_files. Merging goes
  977.  * in numbers of files that can be opened at the same time. (OPEN_FILES)
  978.  */
  979. void files_merge(file_cnt)
  980. /* [<][>][^][v][top][bottom][index][help] */
  981. register int file_cnt;          /* Nr_of_files to merge */
  982. {
  983.   register int i;
  984.   int limit;
  985.  
  986.   for (i = 0; i < file_cnt; i += OPEN_FILES) {
  987.         /* Merge last files and store in output file */
  988.         if ((limit = i + OPEN_FILES) >= file_cnt) {
  989.                 open_outfile();
  990.                 limit = file_cnt;
  991.         } else {                /* Merge OPEN_FILES files and store in temp
  992.                          * file */
  993.                 temp_files[16] = file_cnt / 26 + 'a';
  994.                 temp_files[17] = file_cnt % 26 + 'a';
  995.                 if ((out_fd = creat(temp_files, 0644)) < 0)
  996.                         error(TRUE, "Cannot creat ", temp_files);
  997.                 file_cnt++;
  998.         }
  999.         merge(i, limit);
  1000.   }
  1001.  
  1002. /* Cleanup mess */
  1003.   i = (only_merge) ? args_limit - args_offset : 0;
  1004.   while (i < file_cnt) (void) unlink(file_name(i++));
  1005. }
  1006.  
  1007. /* Merge () merges the files between start_file and limit_file. */
  1008. void merge(start_file, limit_file)
  1009. /* [<][>][^][v][top][bottom][index][help] */
  1010. int start_file, limit_file;
  1011. {
  1012.   register MERGE *smallest;     /* Keeps track of smallest line */
  1013.   register int i;
  1014.   int file_cnt = limit_file - start_file;       /* Nr of files to merge */
  1015.  
  1016. /* Calculate size in core available for file_cnt merge structs */
  1017.   buf_size = MEMORY_SIZE / file_cnt - LINE_SIZE;
  1018.  
  1019.   mbrk(mem_top);                /* First reset mem to lowest loc. */
  1020.   disabled = 0;                 /* All files not done yet */
  1021.  
  1022. /* Set up merge structures. */
  1023.   for (i = start_file; i < limit_file; i++) {
  1024.         smallest = &merge_f[i - start_file];
  1025.         if (!strcmp(file_name(i), "-")) /* File is stdin */
  1026.                 smallest->fd = 0;
  1027.         else if ((smallest->fd = open(file_name(i), O_RDONLY)) < 0) {
  1028.                 smallest->fd = ERROR;
  1029.                 error(FALSE, "Cannot open ", file_name(i));
  1030.                 disabled++;     /* Done this file */
  1031.                 continue;
  1032.         }
  1033.         smallest->buffer = msbrk(buf_size);
  1034.         smallest->line = msbrk(LINE_SIZE);
  1035.         smallest->cnt = smallest->read_chars = 0;
  1036.         (void) read_line(smallest);     /* Read first line */
  1037.   }
  1038.  
  1039.   if (disabled == file_cnt) {   /* Couldn't open files */
  1040.         (void) close(out_fd);
  1041.         return;
  1042.   }
  1043.  
  1044. /* Find a merg struct to assign smallest. */
  1045.   for (i = 0; i < file_cnt; i++) {
  1046.         if (merge_f[i].fd != ERROR) {
  1047.                 smallest = &merge_f[i];
  1048.                 break;
  1049.         }
  1050.   }
  1051.  
  1052. /* Loop until all files minus one are done */
  1053.   while (disabled < file_cnt - 1) {
  1054.         if (uniq)               /* Skip all same lines */
  1055.                 smallest = skip_lines(smallest, file_cnt);
  1056.         else {                  /* Find smallest line */
  1057.                 for (i = 0; i < file_cnt; i++) {
  1058.                         if (merge_f[i].fd == ERROR)
  1059.                                 continue;       /* We've had this one */
  1060.                         if (compare(merge_f[i].line, smallest->line) < 0)
  1061.                                 smallest = &merge_f[i];
  1062.                 }
  1063.         }                       /* Print line and read next */
  1064.         smallest = print(smallest, file_cnt);
  1065.   }
  1066.  
  1067.   if (only_merge && uniq)
  1068.         uniq_lines(smallest);   /* Print only uniq lines */
  1069.   else                          /* Print rest of file */
  1070.         while (print(smallest, file_cnt) != NIL_MERGE);
  1071.  
  1072.   put_line(NIL_PTR);            /* Flush output buffer */
  1073. }
  1074.  
  1075. /* Put_line () prints the line into the out_fd filedescriptor. If line equals
  1076.  * NIL_PTR, the out_fd is flushed and closed.
  1077.  */
  1078. void put_line(line)
  1079. /* [<][>][^][v][top][bottom][index][help] */
  1080. register char *line;
  1081. {
  1082.   static int index = 0;         /* Index in out_buffer */
  1083.  
  1084.   if (line == NIL_PTR) {        /* Flush and close */
  1085.         mwrite(out_fd, out_buffer, index);
  1086.         index = 0;
  1087.         (void) close(out_fd);
  1088.         return;
  1089.   }
  1090.   do {                          /* Fill out_buffer with line */
  1091.         out_buffer[index++] = *line;
  1092.         if (index == IO_SIZE) {
  1093.                 mwrite(out_fd, out_buffer, IO_SIZE);
  1094.                 index = 0;
  1095.         }
  1096.   } while (*line++ != '\n');
  1097. }
  1098.  
  1099. /*
  1100.  * Print () prints the line of the merg structure and tries to read another one.
  1101.  * If this fails, it returns the next merg structure which file_descriptor is
  1102.  * still open. If none could be found, a NIL structure is returned.
  1103.  */
  1104. MERGE *print(merg, file_cnt)
  1105. /* [<][>][^][v][top][bottom][index][help] */
  1106. register MERGE *merg;
  1107. int file_cnt;                   /* Nr of files that are being merged */
  1108. {
  1109.   register int i;
  1110.  
  1111.   put_line(merg->line);         /* Print the line */
  1112.  
  1113.   if (read_line(merg) == ERROR) {       /* Read next line */
  1114.         for (i = 0; i < file_cnt; i++) {
  1115.                 if (merge_f[i].fd != ERROR) {
  1116.                         merg = &merge_f[i];
  1117.                         break;
  1118.                 }
  1119.         }
  1120.         if (i == file_cnt)      /* No more files left */
  1121.                 return NIL_MERGE;
  1122.   }
  1123.   return merg;
  1124. }
  1125.  
  1126. /* Read_line () reads a line from the fd from the merg struct. If the read
  1127.  * failed, disabled is incremented and the file is closed. Readings are
  1128.  * done in buf_size bytes.
  1129.  * Lines longer than LINE_SIZE are silently truncated.
  1130.  */
  1131. int read_line(merg)
  1132. /* [<][>][^][v][top][bottom][index][help] */
  1133. register MERGE *merg;
  1134. {
  1135.   register char *ptr = merg->line - 1;  /* Ptr buf that will hold line */
  1136.  
  1137.   do {
  1138.         ptr++;
  1139.         if (merg->cnt == merg->read_chars) {    /* Read new buffer */
  1140.                 if ((merg->read_chars =
  1141.                      read(merg->fd, merg->buffer, buf_size)) <= 0) {
  1142.                         (void) close(merg->fd); /* OOPS */
  1143.                         merg->fd = ERROR;
  1144.                         disabled++;
  1145.                         return ERROR;
  1146.                 }
  1147.                 merg->cnt = 0;
  1148.         }
  1149.         *ptr = merg->buffer[merg->cnt++];       /* Assign next char of line */
  1150.         if (ptr - merg->line == LINE_SIZE - 1)
  1151.                 *ptr = '\n';    /* Truncate very long lines */
  1152.   } while (*ptr != '\n' && *ptr != '\0');
  1153.  
  1154.   if (*ptr == '\0')             /* Add '\n' to last line */
  1155.         *ptr = '\n';
  1156.   *++ptr = '\0';                /* Add '\0' */
  1157.   return OK;
  1158. }
  1159.  
  1160. /* Skip_lines () skips all same lines in all the files currently being merged.
  1161.  * It returns a pointer to the merge struct containing the smallest line.
  1162.  */
  1163. MERGE *skip_lines(smallest, file_cnt)
  1164. /* [<][>][^][v][top][bottom][index][help] */
  1165. register MERGE *smallest;
  1166. int file_cnt;
  1167. {
  1168.   register int i;
  1169.   int ret;
  1170.  
  1171.   if (disabled == file_cnt - 1) /* We've had all */
  1172.         return smallest;
  1173.  
  1174.   for (i = 0; i < file_cnt; i++) {
  1175.         if (merge_f[i].fd == ERROR || smallest == &merge_f[i])
  1176.                 continue;       /* Don't check same file */
  1177.         while ((ret = compare(merge_f[i].line, smallest->line)) == 0) {
  1178.                 if (read_line(&merge_f[i]) == ERROR) break;     /* EOF */
  1179.         }
  1180.         if (ret < 0)            /* Line wasn't smallest. Try again */
  1181.                 return skip_lines(&merge_f[i], file_cnt);
  1182.   }
  1183.   return smallest;
  1184. }
  1185.  
  1186. /* Uniq_lines () prints only the uniq lines out of the fd of the merg struct. */
  1187. void uniq_lines(merg)
  1188. /* [<][>][^][v][top][bottom][index][help] */
  1189. register MERGE *merg;
  1190. {
  1191.   char lastline[LINE_SIZE];     /* Buffer to hold last line */
  1192.  
  1193.   for (;;) {
  1194.         put_line(merg->line);   /* Print this line */
  1195.         copy(lastline, merg->line);     /* and save it */
  1196.         if (read_line(merg) == ERROR)   /* Read the next */
  1197.                 return;
  1198.         /* Keep reading until lines duffer */
  1199.         while (compare(lastline, merg->line) == SAME)
  1200.                 if (read_line(merg) == ERROR) return;
  1201.   }
  1202.  
  1203.   /* NOTREACHED */
  1204. }
  1205.  
  1206. /*
  1207.  * Check_file () checks if a file is sorted in order according to the arguments
  1208.  * given in main ().
  1209.  */
  1210. void check_file(fd, file)
  1211. /* [<][>][^][v][top][bottom][index][help] */
  1212. int fd;
  1213. char *file;
  1214. {
  1215.   register MERGE *merg;         /* 1 file only */
  1216.   char lastline[LINE_SIZE];     /* Save last line */
  1217.   register int ret;             /* ret status of compare */
  1218.  
  1219.   if (fd == 0) file = "stdin";
  1220.   merg = (MERGE *) mem_top;     /* Assign MERGE structure */
  1221.   merg->buffer = mem_top + sizeof(MERGE);
  1222.   merg->line = msbrk(LINE_SIZE);
  1223.   merg->cnt = merg->read_chars = 0;
  1224.   merg->fd = fd;
  1225.   buf_size = MEMORY_SIZE - sizeof(MERGE);
  1226.  
  1227.   if (read_line(merg) == ERROR) /* Read first line */
  1228.         return;
  1229.   copy(lastline, merg->line);   /* and save it */
  1230.  
  1231.   for (;;) {
  1232.         if (read_line(merg) == ERROR)   /* EOF reached */
  1233.                 break;
  1234.         if ((ret = compare(lastline, merg->line)) > 0) {
  1235.                 error(FALSE, "Disorder in file ", file);
  1236.                 write(2, merg->line, length(merg->line));
  1237.                 break;
  1238.         } else if (ret < 0)     /* Copy if lines not equal */
  1239.                 copy(lastline, merg->line);
  1240.         else if (uniq) {
  1241.                 error(FALSE, "Non uniq line in file ", file);
  1242.                 write(2, merg->line, length(merg->line));
  1243.                 break;
  1244.         }
  1245.   }
  1246.  
  1247.   mbrk(mem_top);                /* Reset mem */
  1248. }
  1249.  
  1250. /* Length () returns the length of the argument line including the linefeed. */
  1251. int length(line)
  1252. /* [<][>][^][v][top][bottom][index][help] */
  1253. register char *line;
  1254. {
  1255.   register int i = 1;           /* Add linefeed */
  1256.  
  1257.   while (*line++ != '\n') i++;
  1258.   return i;
  1259. }
  1260.  
  1261. /* Copy () copies the src line into the dest line including linefeed. */
  1262. void copy(dest, src)
  1263. /* [<][>][^][v][top][bottom][index][help] */
  1264. register char *dest, *src;
  1265. {
  1266.   while ((*dest++ = *src++) != '\n');
  1267. }
  1268.  
  1269. /* Msbrk() does a sbrk() and checks the return value. */
  1270. char *msbrk(size)
  1271. /* [<][>][^][v][top][bottom][index][help] */
  1272. register int size;
  1273. {
  1274.   register char *address;
  1275.  
  1276.   address = malloc(size);
  1277.  
  1278.   if (address==0)
  1279.   {
  1280.     error(TRUE, "Not enough memory. Use chmem to allocate more", NIL_PTR);
  1281.   }
  1282.  
  1283.   return address;
  1284. }
  1285.  
  1286. /* Mbrk() does a brk() and checks the return value. */
  1287. void mbrk(address)
  1288. /* [<][>][^][v][top][bottom][index][help] */
  1289. char *address;
  1290. {
  1291.   free(address);
  1292. }
  1293.  
  1294. void catch(dummy)
  1295. /* [<][>][^][v][top][bottom][index][help] */
  1296. int dummy;                      /* to satisfy the prototype */
  1297. {
  1298.   register int i;
  1299.  
  1300.   signal(SIGINT, SIG_IGN);
  1301.   only_merge = FALSE;
  1302.   for (i = 0; i < 26; i++) (void) unlink(file_name(i));
  1303.   do_exit(2);
  1304. }
  1305. /* [<][>][^][v][top][bottom][index][help] */
  1306.